const _ = require('lodash');
const Moment = require('moment');
const Axios = require('axios');
const Models = require('../models');
const Schedule = require('node-schedule');  // schedule定时模块
const logger = require('../utils/logger');
const config = require('../config/config.json');

const { estimateFee, transferEos } = require('../lib/eos');
const acQueue = require('../lib/account-creation-queue');
const { insertLog } = require(__base + '/lib/serviceLogger');

const defaultReceiver = process.env.EOS_ACCOUNT_NAME;  // 默认收款账号
const transferDelayTime = 10000;  // 转账延迟(1秒 = 1000毫秒)

module.exports = (io) => {
  // 每15秒执行一次检测
  Schedule.scheduleJob('*/15 * * * * *', async () => {
    await checkRegister(io);
  });
}

// 获取环境
const fetchEndPoint = () => {
  if (process.env.NODE_ENV === 'production') {
    return process.env.PRODUCTION_ENDPOINT
  } else {
    return process.env.TESTING_ENDPOINT
  }
}

// 检查用户注册
const checkRegister = async (io) => {
  logger.info('Check account register');
  const accountInfos = await checkAccount();

  // 判断是否存在等待注册验证的账号
  if (accountInfos.length > 0) {
    const actionsRet = await fetchActions();

    logger.info('found actions:', actionsRet.length);
    // 遍历所有的交易记录信息(从中挑选出可用的信息)
    for (let item of actionsRet) {
      // 筛选匹配条件
      if (item.action_trace && item.action_trace.act && item.action_trace.act.data && item.action_trace.receipt) {
        // 交易记录数据
        const actionData = item.action_trace.act.data
        const { account } = item.action_trace.act
        const { quantity, to } = actionData
        const { receiver } = item.action_trace.receipt
        const { name: actionName } = item.action_trace.act  // es6的数据结构, 并将name赋值给与actionName

        // 筛选符合条件的交易记录
        if ((quantity && !_.endsWith(quantity, " EOS")) || (receiver !== defaultReceiver) || (actionName !== "transfer") || (account !== 'eosio.token') || (to !== defaultReceiver)) {
          continue;
        }

        // 遍历所有未注册的用户信息, 将其注册
        for (let info of accountInfos) {
          // 不使用await, 异步处理多个账号
          checkAndCreateAccount(actionData, info, io)
        }
      }
    }
  }
}

/** 注册账号
  * actionData: 交易相关的数据
  * info: 数据库存在账号数据
  * io: socket模块
*/
const checkAndCreateAccount = async (actionData, info, io) => {
  let { to, quantity, memo } = actionData

  try {
    if ((to === defaultReceiver) && (info.memo === memo) && info.status === 'initial') {
      logger.info("Received EOS payment:", info.billNo, actionData)
      logger.info(JSON.stringify(info.get({plain: true}), true, 2));
      // 用户已支付的金额
      quantity = Number(quantity.replace("EOS", "").trim()) || 0
      // 先修改状态、避免定时器多次触发该方法(检查到了即为已支付)
      info.status = "paid"
      info.payment = quantity
      await info.save()
      // 创建账号之后所剩的余额
      const { totalCost } = await estimateFee();
      const balance = parseFloat((quantity - totalCost).toFixed(4))

      // 判断余额是否大于0, 若小于0, 证明用户转的资金不足以创建账号所需要消耗的资源
      if (balance >= 0) {
        // 账号创建队列
        await acQueue.add(info);
        // 判断账号是否成功创建
        if (info.status === "complete") {
          io.sendPaidResult(info.billNo, { code: 0, message: 'success', accountName: info.accountName}) // 发送成功注册消息
          // 转账延迟、考虑到区块之间可能还没确认账号
          setTimeout(async () => {
            // 退还剩余资金
            await transferEos(to, info.accountName, `${balance} EOS`, "Refund of created account remaining EOS")
          }, transferDelayTime);
        } else {
          io.sendPaidResult(info.billNo, { code: 412, accountName: info.accountName, message: 'fail to create account' }) // 发送注册失败信息
          insertLog('error', `${info.accountName} fail to create account`)
        }
      } else {
        info.status = "failToCreate"
        await info.save()
        // EOS不足, 账号创建不成功
        io.sendPaidResult(info.billNo, { code: 411, accountName: info.accountName, message: 'EOS not enough, fail to create account' })
        insertLog('error', `${info.accountName} EOS not enough, fail to create account`)
      }
    }
  } catch (err) {
    info.status = "failToCreate"
    await info.save()
    logger.error("checkAndCreateAccount-error", err.message)
    insertLog('error', `${info.accountName} ${err.message}`)
    io.sendPaidResult(info.billNo, { code: 412, accountName: info.accountName, message: 'fail to create account' }) // 发送注册失败信息
  }
}

// 检查库中是否存在未验证的账号(只查询24小时之内创建的)
const checkAccount = async () => {
  try {
    const ret = await Models.AccountCreationRequest.findAll({
      where: {
        status: 'initial',
        createdAt: {
          $gte: Moment().add(-1, "days")
        }
      }
    });
    return ret;
  } catch (err) {
    logger.error("checkAccount-error", err.message)
    return []
  }
}


// 获取eos交易记录
const fetchActions = async () => {
  let actionData = []

  let endpointList = config[process.env.NODE_ENV].endpointList;
  if (process.env.NODE_ENV !== 'production') {
    endpointList = [ {url:process.env.TESTING_ENDPOINT} ]; // 不是production时，只查询测试节点
  }

  for (let item of endpointList) {
    logger.info('Endpoint:', item)
    try {
      const ret = await Axios.post(`${item.url}/v1/history/get_actions`, {
        pos: -1,
        offset: -500,
        account_name: defaultReceiver
      });
      actionData = ret.data.actions ? ret.data.actions : [];
      if (actionData.length > 0) {
        break
      } else {
        continue
      }
    } catch (err) {
      // 获取交易记录接口异常, 返回空数组
      logger.error("fetchActions-error", err.message)
      insertLog('error', err.message)
      continue
    }
  }
  // 所有节点数据均异常, 记录日志
  if (actionData.length == 0) {
    logger.error("fetchActions-error", "all-endpoint-action-data-null")
    insertLog('error', "all-endpoint-action-data-null")
  }
  return actionData
}